package com.heima.media.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.heima.common.aliyun.GreenImageScan;
import com.heima.common.aliyun.GreenTextScan;
import com.heima.common.dto.ResponseResult;
import com.heima.common.dto.User;
import com.heima.common.enums.AppHttpCodeEnum;
import com.heima.common.minio.MinIOService;
import com.heima.common.util.MediaThreadLocalUtil;
import com.heima.common.util.SensitiveWordUtil;
import com.heima.media.dto.ArticleDto;
import com.heima.media.dto.ContentDto;
import com.heima.media.dto.ImageDto;
import com.heima.media.entity.WmChannel;
import com.heima.media.entity.WmNews;
import com.heima.media.entity.WmSensitive;
import com.heima.media.entity.WmUser;
import com.heima.media.feign.ArticleFeign;
import com.heima.media.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

@Service
@Slf4j
public class AuditServiceImpl implements IAuditService {

    @Autowired
    private GreenTextScan textScan;

    @Autowired
    private GreenImageScan imageScan;

    @Autowired
    private MinIOService minIOService;

    @Autowired
    private ArticleFeign articleFeign;

    @Autowired
    private IWmNewsService newsService;

    @Autowired
    private IWmUserService userService;

    @Autowired
    private IWmChannelService channelService;

    @Override
    @Async // 异步调用
    public void audit(WmNews wmNews) {
        System.out.println(Thread.currentThread().getName() + "自动审核服务开始执行");

        // 判断文章的状态
        // 如果文章的状态是8
        if (wmNews.getStatus() == 8) {
            saveArticle(wmNews);
            return;
        }
        // 如果文章的状态是1
        // 提取内容中的文本和图片
        Map<String, Object> contentMap = getTextAndImagesFromContent(wmNews.getContent());
        // 获取文本内容
        String text = (String) contentMap.get("text");
        // 获取内容图片集合
        List<ImageDto> contentImages = (List<ImageDto>) contentMap.get("images");
        // 在调用第三方服务前,先进行本地词库的一个过滤
        boolean termResult = checkTerm(wmNews, text);
        if (!termResult) return;
        // 调用阿里云文本审核
        boolean textResult = checkText(wmNews, text);
        // 调用阿里云图片审核
        if (!textResult) return;
        boolean imageResult = checkImage(wmNews, contentImages);
        // 调用远程接口保存文章
        if (!imageResult) return;
        // 全部审核通过后,需要判断当前文章的发布时间是否小于等于当前的系统时间
        if (wmNews.getPublishTime().getTime() <= System.currentTimeMillis()) {
            saveArticle(wmNews);
        } else {
            // 否则发布延迟消息到redis
            // 获取一个阻塞队列  指定队列的名称
            RBlockingQueue<String> blockingQueue = redissonClient.getBlockingQueue("article_publish");
            // 创建一个延迟队列 指定使用哪个阻塞队列
            RDelayedQueue<String> delayedQueue = redissonClient.getDelayedQueue(blockingQueue);
            // 发送延迟消息 指定消息内容  延迟时间  时间单位
            // 延迟时间  =  文章发布时间 - 当前系统时间
            long time = wmNews.getPublishTime().getTime() - System.currentTimeMillis();
            delayedQueue.offerAsync(JSON.toJSONString(wmNews), time, TimeUnit.MILLISECONDS);
            // 审核通过,还不到发布时间,需要修改状态为8
            wmNews.setStatus(8);
            newsService.updateById(wmNews);
        }
        System.out.println(Thread.currentThread().getName() + "自动审核服务执行完成");
    }

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private IWmSensitiveService sensitiveService;

    /**
     * 敏感词审核
     *
     * @param wmNews
     * @param text
     * @return
     */
    private boolean checkTerm(WmNews wmNews, String text) {
        // 每次审核查询敏感词表的所有数据,然后判断标题+标签+内容 是否包含其中的字段
        // 在程序启动时查询敏感词表,保存到缓存
        if (SensitiveWordUtil.dictionaryMap.size() == 0) {
            // 从数据库读取数据
            LambdaQueryWrapper<WmSensitive> query = new LambdaQueryWrapper<>();
            // select sensitives from wm_sensitive
            query.select(WmSensitive::getSensitives);
            List<String> words = sensitiveService.listObjs(query, o -> o.toString());
            // 初始化map
            SensitiveWordUtil.initMap(words);
        }
        // 为了减少判断的次数,可以先判断标题+标签
        Map<String, Integer> map = SensitiveWordUtil.matchWords(wmNews.getTitle() + wmNews.getLabels());
        String msg = "";
        if (map.size() == 0) {
            // 标题+标签通过了,再判断文本内容
            Map<String, Integer> textMap = SensitiveWordUtil.matchWords(text);
            if (textMap.size() == 0) {
                return true;
            } else {
                msg = "正文: ";
                for (Map.Entry<String, Integer> entry : textMap.entrySet()) {
                    msg = msg + entry.getKey() + " ";
                }
            }
        } else {
            msg = "标题和标签: ";
            for (Map.Entry<String, Integer> entry : map.entrySet()) {
                msg = msg + entry.getKey() + " ";
            }
        }
        wmNews.setStatus(2);
        wmNews.setReason("敏感词审核不通过: " + msg);
        newsService.updateById(wmNews);
        return false;
    }

    /**
     * 保存文章
     *
     * @param wmNews
     */
    private void saveArticle(WmNews wmNews) {
        // 调用远程接口保存文章
        ArticleDto dto = new ArticleDto();
        BeanUtils.copyProperties(wmNews, dto);
        dto.setId(null);
        // 写入authorId
        // 根据文章表中的userId查询自媒体用户
        WmUser wmUser = userService.getById(wmNews.getUserId());
        dto.setAuthorId(wmUser.getApAuthorId());
        dto.setAuthorName(wmUser.getName());
        WmChannel wmChannel = channelService.getById(dto.getChannelId());
        dto.setChannelName(wmChannel.getName());
        dto.setLayout(wmNews.getType());
        dto.setFlag(0);
        // 前端显示的图片直接使用地址根据逗号分隔 http://xxx.jpg,http://yyy.jpg
        List<ImageDto> list = JSON.parseArray(wmNews.getImages(), ImageDto.class);
        List<String> imageList = new ArrayList<>();
        for (ImageDto imageDto : list) {
            imageList.add(imageDto.getUrl());
        }
        if (imageList.size() > 0) {
            String join = String.join(",", imageList);
            dto.setImages(join);
        }
        ResponseResult<Long> responseResult = articleFeign.saveApArticle(dto);
        if (responseResult.getCode().equals(AppHttpCodeEnum.SUCCESS.getCode())) {
            Long articleId = responseResult.getData();
            // 将文章的id写回到自媒体文章表中
            wmNews.setArticleId(articleId);
            // 修改当前自媒体文章表的状态为9
            wmNews.setStatus(9);
            newsService.updateById(wmNews);
        }
    }

    /**
     * 阿里云图片审核
     *
     * @param wmNews
     * @param contentImages
     * @return
     */
    private boolean checkImage(WmNews wmNews, List<ImageDto> contentImages) {
        // 如果minio服务器部署有外网地址,可以直接根据url来检测
        // minio部署在内网的话,需要先下载再调用阿里云检测

        // 需要检测的图片包含封面和内容图片
        List<ImageDto> coverImages = JSON.parseArray(wmNews.getImages(), ImageDto.class);
        for (ImageDto coverImage : coverImages) {
            // 如果内容图片中不包含该图片,添加该图片
            if (!contentImages.contains(coverImage)) {
                contentImages.add(coverImage);
            }
        }
        // 如果文章确实没有图片,这个方法直接返回true
        if (contentImages.size() == 0) {
            return true;
        }
        List<byte[]> byteList = new ArrayList<>();
        // 从minio下载图片
        for (ImageDto image : contentImages) {
            InputStream inputStream = minIOService.download(image.getUrl());
            // 将流转换成字节数组
            try {
                byte[] byteArray = IOUtils.toByteArray(inputStream);
                byteList.add(byteArray);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        try {
            Map map = imageScan.imageScan(byteList);
            // 获取结果
            String suggestion = (String) map.get("suggestion");
            switch (suggestion) {
                case "pass":
                    return true;
                // break;
                case "block":
                    // 修改状态为2 并且记录不通过的原因
                    wmNews.setStatus(2);
                    wmNews.setReason("阿里云图片审核失败: " + (String) map.get("label"));
                    newsService.updateById(wmNews);
                    break;
                case "review":
                    // 修改状态为3 等待人工审核
                    wmNews.setStatus(3);
                    newsService.updateById(wmNews);
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 提取内容中的文本和图片
     *
     * @param content
     * @return
     */
    private Map<String, Object> getTextAndImagesFromContent(String content) {
        Map<String, Object> contentMap = new HashMap<>();
        // 定义文本内容
        StringBuilder stringBuilder = new StringBuilder();
        // 定义图片集合
        List<ImageDto> list = new ArrayList<>();
        // 从内容中解析JSON
        List<ContentDto> dtos = JSON.parseArray(content, ContentDto.class);
        for (ContentDto dto : dtos) {
            if (dto.getType().equals("text")) {
                stringBuilder.append(dto.getValue());
            } else {
                list.add(new ImageDto(dto.getId(), dto.getValue()));
            }
        }
        contentMap.put("text", stringBuilder.toString());
        contentMap.put("images", list);
        return contentMap;
    }

    /**
     * 阿里云文本审核
     *
     * @param wmNews
     * @return
     */
    private boolean checkText(WmNews wmNews, String text) {
        // 审核内容包含文章标题和文章内容中的文本内容+标签
        String content = wmNews.getTitle() + text + wmNews.getLabels();
        // 调用阿里云服务
        try {
            Map map = textScan.greenTextScan(content);
            // 获取结果
            String suggestion = (String) map.get("suggestion");
            switch (suggestion) {
                case "pass":
                    return true;
                // break;
                case "block":
                    // 修改状态为2 并且记录不通过的原因
                    wmNews.setStatus(2);
                    wmNews.setReason("阿里云文本审核失败: " + (String) map.get("label"));
                    newsService.updateById(wmNews);
                    break;
                case "review":
                    // 修改状态为3 等待人工审核
                    wmNews.setStatus(3);
                    newsService.updateById(wmNews);
                    break;
            }
        } catch (Exception e) {
            log.error(e.getMessage());
            e.printStackTrace();
        }
        return false;
    }
}
